#include"Url.h"
#include<string>
#include"StringUtil.h"
#include<iostream>
#include<cstring>


using namespace std;

/*  Constructor: Takes a url and it's starting address (for relative)
 *  and stores the Url in it's different parts and resolves relative
 */

Url::Url(const string U, const string BaseUrl)  {
    Construct(U,BaseUrl);

}


//Constructor: Takes a url string and will assume it is absolute
//If it is not, it will not parse correctly
Url::Url(const string U)  {
    string empty("");
    Construct(U,empty);
}

void Url::Construct(const string U, const string BaseUrl)  {
    string Url = U;			//We may change the url
    StringUtil::Trim(Url);
    if (IsAbsolute(Url))
        ParseUrl(Url);
    else
    {
        Resolve(Url, BaseUrl);
        ParseUrl(Url);
    }
}
//A String is absolute if it begins with file: or http://
bool Url::IsAbsolute(const string Url) const {
    const string Copy = StringUtil::ToLowerCopy(Url);

    return  StringUtil::IsPrefix(Copy, File)||
            StringUtil::IsPrefix(Copy, Http);

}

void Url::ParseUrl(const string u)  {
    const char * Url = u.c_str();		//It's easier to parse a char[]
    int Index = 0;		//Will expect the index to change
    ParseScheme(Url, Index);
    ParseNet(Url,Index);
    ParsePath(Url,Index);
    ParseParam(Url,Index);
    ParseQuery(Url,Index);
    ParseFragment(Url,Index);
    //We are done with the index.
}

void Url::Resolve(string & p, const string Base)  {
    if (Base =="")
        return;
    string NewPath("");
    const char * path = p.c_str();
    Url UrlBase(Base);
    char BasePath[UrlBase.GetPath().size()+1];
    memset(BasePath,'\0',UrlBase.GetPath().size()+1);
    UrlBase.GetPath().copy(BasePath,UrlBase.GetPath().size());
    for (int count = UrlBase.GetPath().size(); count >0; count--){
        if (BasePath[count]!='/')
            BasePath[count] ='\0';
        else
            break;
    }


    if (path[0] == '/'){
        NewPath.append(path);
    } else if (path[0] == '#'){
        NewPath.append(UrlBase.GetPath());
        NewPath.append(path);
    }else{
        NewPath.append(BasePath);
        NewPath.append(path);
    }
    NewPath = ResolveDots(NewPath);

    p = UrlBase.GetScheme();
    p.append(UrlBase.GetNet_Loc());
    p.append(NewPath);


}

void Url::ParseScheme(const char * Url , int & Index)  {
    while ( Url [Index] != '/' &&
            Url[Index] != '\0'){
        Scheme.append(GetNext(Url,Index));
        (Index)++;
    }
    StringUtil::ToLower(Scheme);		//for the comparison
    if (Scheme =="http:")
        while (!isalnum(Url [Index]))
        {
            Scheme.append(GetNext(Url,Index));

            (Index)++;
        }
    StringUtil::ToLower(Scheme);		//The scheme is case insensitive


}

void Url:: ParseNet(const char * Url, int & Index)  {
    if (IsHttp())
    {
        while ( Url [Index] !='/' &&
                Url [Index] !='\0')
        {
            Net_Loc.append(GetNext(Url,Index));
            (Index)++;
        }
        StringUtil::ToLower(Net_Loc);
    }

    /*	::Wrong parsing. The file url type does not have a net_loc::
     	else
    	{
    		while(Url[*Index] !='#' &&
    		      Url[*Index] != '\0')
    			{
    				Net_Loc.append(GetNext(Url,Index));
    				(*Index)++;
    			}
    	}*/


}
void Url:: ParsePath(const char * Url, int & Index)  {
    if (IsHttp())
    {
        while (	Url [Index] !=';' &&
                Url [Index] !='?' &&
                Url [Index] !='#' &&
                Url [Index] !='\0')
        {
            Path.append(GetNext(Url,Index));
            (Index)++;
        }
    }

    if (IsFile())
    {
        while ( 	Url[Index] !='#' &&
                 Url[Index] !='\0')
        {
            Path.append(GetNext(Url,Index));
            (Index)++;
        }



    }

    if (Path == "")
        Path.append("/");

}
void Url:: ParseParam(const char * Url, int & Index)  {
    if ( IsHttp() &&
            Url[Index] ==';')
    {
        while (	Url [Index] !='?' &&
                Url [Index] !='#' &&
                Url [Index] !='\0')
        {
            Param.append(GetNext(Url,Index));
            (Index)++;
        }



    }

}

void Url:: ParseQuery(const char * Url, int & Index)  {
    if ( IsHttp() &&
            Url[Index] =='?')
    {
        while (	Url [Index] !='#' &&
                Url [Index] !='\0')
        {
            Query.append(GetNext(Url,Index));
            (Index)++;
        }



    }

}

void Url:: ParseFragment(const char * Url, int & Index)  {

}
string Url::ResolveDots(const string & p)  {
    string NewPath(p);
    int size = p.size();		//used to keep track of the size of the string of the c string
    char path[size];
    char * FirstB = NULL;
    char * SndB   = NULL;
    string ReturnString;

    for (int count = 0; count<size; count++)	//used to copy the string into a c string, which can be edited
        path[count] = p.at(count);
    path[size] = '\0';				//so the c string is null termincated

    //Find the parenthasis around a ../ or ./
    FirstB = path;
    SndB   = path;					//Part of the finding

    do
    {
        while (*FirstB != '/' && FirstB != &path[size-1])
            FirstB++;
        if (FirstB != &path[size-1])
            SndB = FirstB +1;

        while (*SndB != '/' &&   SndB != &path[size-1])
            SndB++;

        if (SndB - FirstB  == 2)
            if (FirstB[1] =='.')
                break;
        if (SndB - FirstB == 3)
            if (FirstB[1] =='.' && FirstB[2] =='.')
                break;

        if (FirstB != &path[size-1])
            FirstB++;

    }
    while (FirstB != &path[size-1]);




    if (FirstB == &path[size-1])			//When we each the end of the string return
        return p;

    if (FirstB[1] =='.' && FirstB[2] =='.')		//Resolve the ../
    {
        char* pointer = FirstB;
        if (pointer != path)
            pointer --;
        while (pointer != path && *pointer !='/')
            pointer --;
        //strcpy(pointer,SndB);
        //*pointer = '\0';

        do
        {
            *(pointer++) = *(SndB);
        }
        while (*(SndB++) !='\0');

        //ReturnString.append(path);
        //ReturnString.append(SndB);

    }
    else						//Resolves ./
    {
        //strcpy(FirstB,SndB);
        do
        {
            *(FirstB++) = *(SndB);
        }
        while (*(SndB++) !='\0');

    }

    //cout<<path<<endl;
    //	string ReturnString(path);
    ReturnString = path;
    return ResolveDots(ReturnString);
}

string Url:: GetScheme () const {
    return Scheme;
}

string Url:: GetNet_Loc () const {
    return Net_Loc;
}

string Url:: GetPath () const {
    return Path;
}
string Url::GetParam	() const {
    return Param;
}
string Url::GetQuery	() const {
    return Query;
}
string Url::GetFragment	() const {
    return Fragment;
}

string Url::GetNext(const char * Url, const int & Index)  {
    const char read [] ={Url[Index],'\0'};			//c string to convert to a string


    string readString(read);
    return readString;
}
bool Url::IsHttp()  {
    return GetScheme() == Http;
}

bool Url::IsFile()  {
    return GetScheme() == File;
}
bool Url::IsValid()  {

    return  IsFile()||
            IsHttp();
}

string Url::ToString() const {
    string ReturnString;
    ReturnString.append(GetScheme());
    ReturnString.append(GetNet_Loc());
    ReturnString.append(GetPath());
    ReturnString.append(GetParam());
    ReturnString.append(GetQuery());
    ReturnString.append(GetFragment());


    return ReturnString;
}


string Url::GetBaseUrl(const Url Path) const {

    char BasePath[Path.GetPath().size()+1];// = UrlBase.GetPath().c_str();
    memset(BasePath,'\0',Path.GetPath().size()+1);
    Path.GetPath().copy(BasePath,Path.GetPath().size());
    //	cout<<BasePath<<endl;
    for (int count = Path.GetPath().size(); count >0; count--)
    {
        if (BasePath[count]!='/')
            BasePath[count] ='\0';
        else
            break;
    }
    string ReturnValue(Path.GetScheme());
    ReturnValue.append(Path.GetNet_Loc());
    ReturnValue.append(BasePath);


    return ReturnValue;
}

bool Url::Test(ostream & os){
	bool success = true;
	Url url("http://www.byu.edu/this/is/a/path/index.html;lang=engl?team=Dallas#Basketball");
	//Test basic functionality
	TEST(url.GetPath() == "/this/is/a/path/index.html");
	TEST(url.GetNet_Loc() == "www.byu.edu");
	TEST(url.IsHttp() == true);
	TEST(url.ToString()=="http://www.byu.edu/this/is/a/path/index.html;lang=engl?team=Dallas");

	//Tests if I am resolving relative paths correctly
	Url url2("../.././../newPath","http://www.byu.edu/this/is/a/path/index.html;lang=engl?team=Dallas#Basketball");
	TEST(url2.ToString() =="http://www.byu.edu/this/newPath" );
	//Test for capital letters
	Url url3("/","Http://www.byu.edu/this/is/a/path/index.html;lang=engl?team=Dallas#Basketball");
	TEST(url3.ToString() =="http://www.byu.edu/" );

	//Test File system detection
	Url url4("file:///home/town/html.html");
	TEST(url4.IsFile() == true);
	return success;
}
